﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Reflection;
using System.Web.Mvc;

namespace Web.Resource
{
    /// <summary>
    /// 资源文件统一管理类
    /// 提供对资源文件的保存、分析和其它常用操作
    /// 支持可配置化是否以月份分隔资源文件
    /// <remarks>作者：陈国伟 日期：2013/07/06</remarks>
    /// </summary>
    public sealed class ResManage
    {
        /// <summary>
        /// 上传目录的文件夹名
        /// </summary>
        private readonly static string UPLOADFLODER = "upload";

        /// <summary>
        /// 是否以月份来分开资源文件
        /// </summary>
        private readonly static bool ResSplitByMonth = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["ResSplitByMonth"]);

        /// <summary>
        /// 获取当前月份信息
        /// </summary>
        private static string CurrentMonth
        {
            get
            {
                if (ResManage.ResSplitByMonth == true)
                {
                    return DateTime.Now.ToString("yyyy_MM");
                }
                return string.Empty;
            }
        }

        /// <summary>
        /// 获取上传目录的本地完整路径
        /// </summary>
        public static string UploadFullPath
        {
            get
            {
                return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ResManage.UPLOADFLODER);
            }
        }

        /// <summary>
        /// 获取上传目录的相对网络路径
        /// </summary>
        public static string UploadUrl
        {
            get
            {
                return string.Concat("/", ResManage.UPLOADFLODER, "/");
            }
        }
        /// <summary>
        /// 获取上传目录的完整网络路径
        /// 此属性依赖于HttpConext
        /// </summary>
        public static string UploadFullUrl
        {
            get
            {
                return string.Concat(HttpContext.Current.Request.Url.Scheme, "://", HttpContext.Current.Request.Url.Authority, ResManage.UploadUrl);
            }
        }


        /// <summary>
        /// 从文件名获得资源信息对象
        /// </summary>
        /// <param name="fileName">本地或web文件名[包含ResType]</param>
        /// <exception cref="ArgumentNullException">fileName</exception>
        /// <returns></returns>
        public static ResManage Parse(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException();
            }
            return new ResManage(fileName);
        }

        /// <summary>
        /// 从文件名获得资源信息对象
        /// </summary>
        /// <param name="fileName">本地或web文件名[包含或不包含ResType]</param>
        /// <param name="resType">指定资源类型</param>
        /// <exception cref="ArgumentNullException">fileName</exception>
        public static ResManage Parse(string fileName, ResType resType)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException();
            }
            return new ResManage(fileName, resType);
        }


        /// <summary>
        /// 获取提供此资源下载的ActionResult对象
        /// </summary>
        /// <param name="fileFullName">文件完整路径名</param>
        /// <returns></returns>
        public static ActionResult GetFileResult(string fileFullName)
        {
            if (File.Exists(fileFullName) == false)
            {
                return new ContentResult() { Content = "您要下载的文件不存在..." };
            }

            using (var stream = new FileStream(fileFullName, FileMode.Open, FileAccess.Read))
            {
                var fileName = HttpUtility.UrlEncode(Path.GetFileName(fileFullName));
                var response = HttpContext.Current.Response;
                response.ContentType = "application/octet-stream";
                response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
#if RELEASE
                response.AddHeader("Content-Length", stream.Length.ToString());
#endif
                var buffer = new byte[1024 * 100];
                int readLength = 0;
                while ((readLength = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    if (response.IsClientConnected)
                    {
                        response.OutputStream.Write(buffer, 0, readLength);
                        response.Flush();
                    }
                    else
                    {
                        break;
                    }
                }
                response.Close();
                response.End();
            }
            return new EmptyResult();
        }

        /// <summary>
        /// 保存上传的文件对象到指定类型文件夹下
        /// 返回保存后资源文件的信息
        /// </summary>
        /// <param name="file">上传的文件</param>
        /// <param name="resType">资源类型</param>
        /// <param name="idName">用以作文件名或文件的父文件夹名</param>
        /// <param name="useIdFolder">是否使用idName作父文件夹包裹文件，如果为ture,文件名将不变</param>
        /// <param name="keepName">当useIdFolder为true且keepName为false时，文件名为随机名</param>
        /// <returns></returns>
        public static ResManage SavePostedFile(HttpPostedFileBase file, ResType resType, Guid idName, bool useIdFolder = false, bool keepName = false)
        {
            return ResManage.SaveFile(file, resType, idName, useIdFolder, keepName);
        }

        /// <summary>
        /// 保存上传的文件对象到指定类型文件夹下
        /// 返回保存后资源文件的信息
        /// </summary>
        /// <param name="file">上传的文件</param>
        /// <param name="resType">资源类型</param>
        /// <param name="idName">用以作文件名或文件的父文件夹名</param>
        /// <param name="useIdFolder">是否使用idName作父文件夹包裹文件，如果为ture,文件名将不变</param>
        /// <param name="keepName">当useIdFolder为true且keepName为false时，文件名为随机名</param>
        /// <returns></returns>
        public static ResManage SavePostedFile(HttpPostedFileBase file, ResType resType, int idName, bool useIdFolder = false, bool keepName = false)
        {
            return ResManage.SaveFile(file, resType, idName, useIdFolder, keepName);
        }

        /// <summary>
        /// 保存上传的文件对象到指定类型文件夹下
        /// 返回保存后资源文件的信息
        /// </summary>
        /// <param name="file">上传的文件</param>
        /// <param name="resType">资源类型</param>
        /// <param name="idName">用以作文件名或文件的父文件夹名</param>
        /// <param name="useIdFolder">是否使用idName作父文件夹包裹文件，如果为ture,文件名将不变</param>
        /// <param name="keepName">当useIdFolder为true且keepName为false时，文件名为随机名</param>
        /// <returns></returns>
        private static ResManage SaveFile(HttpPostedFileBase file, ResType resType, object idName, bool useIdFolder = false, bool keepName = false)
        {
            if (file == null || file.ContentLength == 0)
            {
                throw new ArgumentNullException("file");
            }

            if (resType == default(ResType))
            {
                throw new ArgumentException("resType不能为" + resType.ToString());
            }

            var idNameString = idName.ToString().Replace("-", "_");
            string fileName = string.Concat(idNameString, Path.GetExtension(file.FileName));

            if (useIdFolder == true)
            {
                if (keepName == true)
                {
                    fileName = Path.Combine(idNameString, file.FileName);
                }
                else
                {
                    var randomName = string.Concat(Guid.NewGuid().ToString().Replace("-", "_"), Path.GetExtension(file.FileName));
                    fileName = Path.Combine(idNameString, randomName);
                }
            }
            if (ResManage.ResSplitByMonth == true)
            {
                fileName = Path.Combine(ResManage.CurrentMonth, fileName);
            }

            string fullFileName = Path.Combine(ResManage.UploadFullPath, resType.ToString(), fileName);
            string fullPath = Path.GetDirectoryName(fullFileName);
            Directory.CreateDirectory(fullPath);
            file.SaveAs(fullFileName);

            return ResManage.Parse(fileName, resType);
        }

        /// <summary>
        /// 获取当前的资源信息的状态
        /// </summary>
        private bool ResStatus
        {
            get
            {
                return this.ResType != default(ResType) && string.IsNullOrEmpty(this.FileName) == false;
            }
        }

        /// <summary>
        /// 获取或设置资源的类型
        /// </summary>
        public ResType ResType { get; set; }

        /// <summary>
        /// 获取资源的短文件名
        /// 对应保存到数据库的文件名
        /// </summary>
        public string FileName { get; private set; }

        /// <summary>
        /// 获取资源的完整文件名
        /// </summary>
        public string FullFileName
        {
            get
            {
                if (this.ResStatus)
                {
                    return Path.Combine(ResManage.UploadFullPath, this.ResType.ToString(), this.FileName);
                }
                return string.Empty;
            }
        }
        /// <summary>
        /// 获取资源的web相对路径
        /// </summary>
        public string UrlName
        {
            get
            {
                if (this.ResStatus)
                {
                    return string.Concat(ResManage.UploadUrl, this.ResType, "/", this.FileName.Replace(@"\", "/"));
                }
                return string.Empty;
            }
        }
        /// <summary>
        /// 获取资源的完整web路径
        /// </summary>
        public string FullUrlName
        {
            get
            {
                if (this.ResStatus)
                {
                    return string.Concat(ResManage.UploadFullUrl, this.ResType, "/", this.FileName.Replace(@"\", "/"));
                }
                return string.Empty;
            }
        }
        /// <summary>
        /// 获取资源文件是否存在
        /// </summary>
        public bool Exist
        {
            get
            {
                return File.Exists(this.FullFileName);
            }
        }

        /// <summary>
        /// 构造器
        /// </summary>
        /// <param name="fileName">文件本地或web路径(包含ResType)</param>
        private ResManage(string fileName)
        {
            fileName = HttpUtility.UrlDecode(fileName);
            var inputString = fileName.Replace("/", @"\");
            this.FileName = this.GetFileName(inputString);
            this.ResType = this.GetResType(inputString);
        }

        /// <summary>
        /// 构造器
        /// </summary>
        /// <param name="fileName">文件本地或web路径(包含或不包含ResType)</param>
        /// <param name="resType">资源类型</param>      
        private ResManage(string fileName, ResType resType)
        {
            fileName = HttpUtility.UrlDecode(fileName);
            var inputString = fileName.Replace("/", @"\");
            this.FileName = this.GetFileName(inputString);
            this.ResType = resType;
        }

        /// <summary>
        /// 分析和获取文件名
        /// </summary>
        /// <param name="input">输入的文件路径信息</param>
        private string GetFileName(string input)
        {
            if (ResManage.ResSplitByMonth == false)
            {
                var p1 = @"\w{8}_\w{4}_\w{4}_\w{4}_\w{12}\\.+?\.\w{2,4}$";
                var p2 = @"\w{8}_\w{4}_\w{4}_\w{4}_\w{12}\.\w{2,4}$";
                var p3 = @"\d+\\.+?\.\w{2,4}$";
                var p4 = @"\d+\.\w{2,4}$";
                return input.Match(string.Join("|", p1, p2, p3, p4));
            }
            else
            {
                var p1 = @"\d{4}_\d{2}\\\w{8}_\w{4}_\w{4}_\w{4}_\w{12}\\.+?\.\w{2,4}$";
                var p2 = @"\d{4}_\d{2}\\\w{8}_\w{4}_\w{4}_\w{4}_\w{12}\.\w{2,4}$";
                var p3 = @"\d{4}_\d{2}\\\d+\\.+?\.\w{2,4}$";
                var p4 = @"\d{4}_\d{2}\\\d+\.\w{2,4}$";
                return input.Match(string.Join("|", p1, p2, p3, p4));
            }
        }

        /// <summary>
        /// 分析和获取资源类型
        /// </summary>
        /// <param name="input">输入的文件路径信息</param>
        private ResType GetResType(string input)
        {
            var resType = input.Replace(this.FileName, string.Empty).Match(@"\w+\\$").Replace(@"\", string.Empty).ToLower();
            var resTypeField = typeof(ResType).GetFields(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(f => f.Name.ToLower().Equals(resType));
            if (resTypeField != null)
            {
                resType = resTypeField.Name;
                return (ResType)Enum.Parse(typeof(ResType), resType);
            }
            return default(ResType);
        }

        /// <summary>
        /// 删除包含的资源文件
        /// 不存在不会报错
        /// </summary>
        public void Delete()
        {
            if (this.Exist)
            {
                File.Delete(this.FullFileName);
            }
        }

        /// <summary>
        /// 复制到指定类型资源的路径中
        /// 如果指定文件存在，则覆盖
        /// 成功则返回true
        /// </summary>
        /// <param name="resType">目标类型</param>
        /// <returns></returns>
        public bool CopyTo(ResType resType)
        {
            if (this.Exist && resType != default(ResType) && resType != this.ResType)
            {
                var destFileName = Path.Combine(ResManage.UploadFullPath, resType.ToString(), this.FileName);
                var destDir = Path.GetDirectoryName(destFileName);
                Directory.CreateDirectory(destDir);

                File.Copy(this.FullFileName, destFileName, true);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 移动到指定类型资源的路径中
        /// 如果指定文件存在，则覆盖
        /// 返回新目标资源
        /// </summary>
        /// <param name="resType">目标类型</param>
        /// <returns></returns>
        public ResManage MoveTo(ResType resType)
        {
            if (this.ResType != resType)
            {
                this.CopyTo(resType);
                this.Delete();
                this.ResType = resType;
            }
            return this;
        }

        /// <summary>
        /// 重命名资源文件名
        /// 成功则返回true
        /// </summary>
        /// <param name="newName">新文件名</param>
        /// <returns></returns>
        public bool ReName(string newName)
        {
            if (this.Exist)
            {
                newName = Path.GetFileNameWithoutExtension(newName.NullThenEmpty());
                if (string.IsNullOrEmpty(newName) == false)
                {
                    var dir = Path.GetDirectoryName(this.FileName);
                    newName = string.Concat(newName, Path.GetExtension(this.FileName));

                    var sourceFile = this.FullFileName;
                    this.FileName = Path.Combine(dir, newName);
                    File.Move(sourceFile, this.FullFileName);
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 转换为保存到数据库的文件名
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.FileName;
        }

        /// <summary>
        /// 获取扩展名对应的枚举类型的值
        /// </summary>
        /// <typeparam name="T">格式枚举类型</typeparam>
        /// <returns></returns>
        public int GetFormatEnumValue<T>() where T : struct
        {
            var ext = Path.GetExtension(this.FileName).TrimStart('.').ToLower();
            var fieldName = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public).Select(item => item.Name.ToLower()).FirstOrDefault(item => item.Equals(ext));
            return Enum.Parse(typeof(T), fieldName).GetHashCode();
        }

        /// <summary>
        /// 获取提供此资源下载的ActionResult对象
        /// </summary>      
        /// <returns></returns>
        public ActionResult GetFileResult()
        {
            return ResManage.GetFileResult(this.FullFileName);
        }
    }
}